#include "monitor/monitor.h"
#include "monitor/expr.h"
#include "monitor/watchpoint.h"
#include "temu.h"
#include "cpu/helper.h"
#include <setjmp.h>
#include <stdlib.h>
#include <readline/readline.h>
#include <readline/history.h>

void cpu_exec(uint32_t);

void display_reg();

/* We use the `readline' library to provide more flexibility to read from stdin. */
char* rl_gets() {
	static char *line_read = NULL;

	if (line_read) {
		free(line_read);
		line_read = NULL;
	}

	line_read = readline("(temu) ");

	if (line_read && *line_read) {
		add_history(line_read);
	}

	return line_read;
}

static int cmd_c(char *args) {
	cpu_exec(-1);
	return 0;
}

static int cmd_q(char *args) {
	return -1;
}


//command si
static int cmd_si(char *args) {
	// char *arg = strtok(NULL, " ");

	int exec_steps;

	if (args == NULL){
		exec_steps = 1;
	}else{
		exec_steps = atoi(args);
	}

	// if(arg != NULL) {
	// 	sscanf(arg, "%d", &exec_steps);
	// }
	if( exec_steps == 0 ){
		exec_steps = 1;
	}

	cpu_exec(exec_steps);
	
	return 0;
}

//command info
static int cmd_info(char *args) {
	char *arg = strtok(NULL, " ");

	if(arg != NULL) {
		if(strcmp(arg, "r") == 0) {
			display_reg();
		}
		else if(strcmp(arg, "w") == 0) {
			list_watchpoint();
		}
	}
	return 0;
}

//command add
static int cmd_p(char *args) {
	bool success;

	if(args) {
		uint32_t r = expr(args, &success);
		if(success) { 
			printf("0x%08x(%d)\n", r, r); 
		}
		else {
			printf("Bad expression\n"); 
		}
	}
	return 0;
}

static int cmd_x(char *args)
{
	if(args == NULL)
	{
		return 0;
	}
	int sum;
	char sumrt[100]="";
	char addrt[100]="";


	int cnt = 0;
	int len = strlen(args);
	//length of input
	int i1 = 0 ;
		while(i1 < 100){
			i1++;
		}

	int i;
	for(i=0; i<len; i++)
	{
		cnt = i;
		if(args[i] == ' ')
		{
			break;
		}
		sumrt[i] = args[i];
	}


	if(cnt == len-1)
	{
		printf("format error.Please enter again\n");
		return 0;
	}
	sumrt[cnt] = '\0';
	sum = atoi(sumrt);

	int templen = 0;
	for(i=cnt+1; i<len; i++)
	{
		addrt[templen++] = args[i];
	}
	addrt[templen] = '\0';



	bool *success = (bool*)malloc(sizeof(bool));
	uint32_t addr = expr(addrt, success);
	addr = addr & 0x1fffffff;
	if(sum < 0  || success == false)
	{
		printf("sum error\n");
		return 0;
	}
	char buf[10001];
	if(sum > 10001)
	{
		return -1;
	}
	int i2 = 0 ;
		while(i2 < 100){
			i2++;
		}
	for(i=0; i<sum ;i++)
	{
		sprintf(buf, "%02x ",   instr_fetch(addr+i,1));
		
		if(i%10 == 0 && i!=0)
		{
			printf("\n%#x\t", addr+i );
		}
		else if(i%10 == 0 && i == 0)
		{
			printf("%#x\t", addr );
		}
		printf("%s ", buf);
	}
        printf("\n");
	return 0;
}

static int cmd_w(char *args) {
	if(args) {
		int NO = set_watchpoint(args);
		if(NO != -1) { printf("Set watchpoint #%d\n", NO); }
		else { printf("Bad expression\n"); }
	}
	return 0;
}

static int cmd_d(char *args) {
	int NO;
	sscanf(args, "%d", &NO);
	if(!delete_watchpoint(NO)) {
		printf("Watchpoint #%d does not exist\n", NO);
	}

	return 0;
}

static int cmd_help(char *args);

static struct {
	char *name;
	char *description;
	int (*handler) (char *);
} cmd_table [] = {
	{ "help", "Display informations about all supported commands", cmd_help },
	{ "c", "Continue the execution of the program", cmd_c },
	{ "q", "Exit TEMU", cmd_q },

	/* TODO: Add more commands */
	{ "p", "Evaluate the value of expression", cmd_p },
	{ "info", "info r - print register values; info w - show watch point state", cmd_info },
	{ "si", "Single step", cmd_si },
	{ "x" , "show content in memory" , cmd_x},
	{ "p" , "calculate expression that you input",cmd_p},
	{ "w", "Set watchpoint", cmd_w},
	{"d", "Delete watchpoint", cmd_d}


};

#define NR_CMD (sizeof(cmd_table) / sizeof(cmd_table[0]))

static int cmd_help(char *args) {
	/* extract the first argument */
	char *arg = strtok(NULL, " ");
	int i;

	if(arg == NULL) {
		/* no argument given */
		for(i = 0; i < NR_CMD; i ++) {
			printf("%s - %s\n", cmd_table[i].name, cmd_table[i].description);
		}
	}
	else {
		for(i = 0; i < NR_CMD; i ++) {
			if(strcmp(arg, cmd_table[i].name) == 0) {
				printf("%s - %s\n", cmd_table[i].name, cmd_table[i].description);
				return 0;
			}
		}
		printf("Unknown command '%s'\n", arg);
	}
	return 0;
}

void ui_mainloop() {
	while(1) {
		char *str = rl_gets();
		char *str_end = str + strlen(str);

		/* extract the first token as the command */
		char *cmd = strtok(str, " ");
		if(cmd == NULL) { continue; }

		/* treat the remaining string as the arguments,
		 * which may need further parsing
		 */
		char *args = cmd + strlen(cmd) + 1;
		if(args >= str_end) {
			args = NULL;
		}

		int i;
		for(i = 0; i < NR_CMD; i ++) {
			if(strcmp(cmd, cmd_table[i].name) == 0) {
				if(cmd_table[i].handler(args) < 0) { return; }
				break;
			}
		}

		if(i == NR_CMD) { printf("Unknown command '%s'\n", cmd); }
	}
}
